package com.hyt.it.ogt.ksmonitor.statistics.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.util.DateUtils;
import com.hyt.core.util.UUIDUtils;
import com.hyt.it.ogt.ksmonitor.statistics.enums.MonitorStatisticsTypeCode;
import com.hyt.it.ogt.ksmonitor.statistics.enums.MonitorStatisticsUtils;
import com.hyt.it.ogt.ksmonitor.statistics.mapper.MonitorStatisticsMapper;
import com.hyt.it.ogt.ksmonitor.statistics.model.CandidateStatisticsAddVO;
import com.hyt.it.ogt.ksmonitor.statistics.model.MonitorStatisticsVO;
import com.hyt.it.ogt.ksmonitor.statistics.model.entity.KwMonitorStatistics;
import com.hyt.it.ogt.ksmonitor.statistics.redis.BaseRedisTemplateService;
import com.hyt.it.ogt.ksmonitor.statistics.service.IMonitorStatisticsService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.hyt.it.ogt.ksmonitor.statistics.redis.ConstantRedisKey.KS_MONITOR_STATISTICS_CANDIDATE_KEY;

/**
 * @author 朱振宇
 * @since 2022-12-15
 */
@Service
@ConditionalOnClass(RedisTemplate.class)
public class MonitorStatisticsImpl extends BaseServiceImpl<MonitorStatisticsMapper, KwMonitorStatistics> implements IMonitorStatisticsService {

    private final int singleCandidateLockTimeout = 1;

    @Resource
    private BaseRedisTemplateService baseRedisTemplateService;

    @Override
    public boolean addTotal(CandidateStatisticsAddVO candidateStatisticsAddVO) {
        boolean singleCandidateSet = false;
        String examCandidateKey = baseRedisTemplateService.buildKey(KS_MONITOR_STATISTICS_CANDIDATE_KEY.concat(candidateStatisticsAddVO.getExamId()).concat(":").concat(candidateStatisticsAddVO.getStatisticsType() + "").concat(":"), new int[]{0}, new Object[]{candidateStatisticsAddVO.getCandidateId()});
        try {
            if (candidateStatisticsAddVO.isRepeat() || (singleCandidateSet = baseRedisTemplateService.setIfAbsent(examCandidateKey, candidateStatisticsAddVO.getNum(), singleCandidateLockTimeout, TimeUnit.DAYS))) {
                if(candidateStatisticsAddVO.getNum() < 0 && MonitorStatisticsTypeCode.SUBMIT_PAPER_NUM.getCode() == candidateStatisticsAddVO.getStatisticsType()) {
                    baseRedisTemplateService.delete(examCandidateKey);
                }
                MonitorStatisticsVO updVO = Objects.nonNull(candidateStatisticsAddVO.getRoomId()) ?
                        getMonitorStatisticsByRoomId(candidateStatisticsAddVO.getExamId(), candidateStatisticsAddVO.getRoomId(), candidateStatisticsAddVO.getStatisticsType()):
                        getMonitorStatistics(candidateStatisticsAddVO.getExamId(), candidateStatisticsAddVO.getStatisticsType());
                if (Objects.nonNull(updVO)) {
                    return baseMapper.updateStatisticsTotal(updVO.getId(), candidateStatisticsAddVO.getNum(), LocalDateTime.now()) > 0;
                } else {
                    try {
                        return baseMapper.insert(KwMonitorStatistics.builder()
                                .id(UUIDUtils.randomUUID())
                                .examId(candidateStatisticsAddVO.getExamId())
                                .roomId(candidateStatisticsAddVO.getRoomId())
                                .statisticsType(candidateStatisticsAddVO.getStatisticsType())
                                .total(candidateStatisticsAddVO.getNum())
                                .createDate(LocalDateTime.now())
                                .build()) > 0;
                    } catch (DuplicateKeyException e) {
                        updVO = Objects.nonNull(candidateStatisticsAddVO.getRoomId()) ?
                                getMonitorStatisticsByRoomId(candidateStatisticsAddVO.getExamId(), candidateStatisticsAddVO.getRoomId(), candidateStatisticsAddVO.getStatisticsType()):
                                getMonitorStatistics(candidateStatisticsAddVO.getExamId(), candidateStatisticsAddVO.getStatisticsType());
                        if(updVO == null) {
                            log.error("更新统计数量异常：examId={}，statisticsType={}，num={}" + candidateStatisticsAddVO.getExamId() + "，statisticsType=" + candidateStatisticsAddVO.getStatisticsType() + "，num=" + candidateStatisticsAddVO.getNum());
                        }
                        return baseMapper.updateStatisticsTotal(updVO.getId(), candidateStatisticsAddVO.getNum(), LocalDateTime.now()) > 0;
                    }
                }
            }
        } catch (Exception e) {
           logger.error("监考端数据统计异常", e);
            if (singleCandidateSet) {
                baseRedisTemplateService.delete(examCandidateKey);
            }
        }
        return false;
    }

    @Override
    public boolean addMonitorStatistics(MonitorStatisticsVO monitorStatisticsVO) {
        return baseMapper.insert(KwMonitorStatistics.builder()
                .id(UUIDUtils.randomUUID())
                .examId(monitorStatisticsVO.getExamId())
                .statisticsType(monitorStatisticsVO.getStatisticsType())
                .total(monitorStatisticsVO.getNum())
                .createDate(LocalDateTime.now()).build()) > 0;
    }

    @Override
    public boolean updateMonitorStatisticsNum(String id, long source, long target) {
        return baseMapper.updateStatisticsTotalByVerify(id, source, target, LocalDateTime.now()) > 0;
    }

    @Override
    public List<MonitorStatisticsVO> findMonitorStatisticsList(String examId) {
        List<MonitorStatisticsVO> monitorStatisticsVOList;
        List<KwMonitorStatistics> resList = baseMapper.findMonitorStatisticsList(examId);
        if (!resList.isEmpty()) {
            monitorStatisticsVOList = resList.stream().map(vo -> MonitorStatisticsVO.builder()
                    .examId(examId)
                    .statisticsType(vo.getStatisticsType())
                    .statisticsTypeDesc(MonitorStatisticsTypeCode.findByCode(vo.getStatisticsType()).getMsg())
                    .num(vo.getTotal())
                    .build()).collect(Collectors.toList());
        } else {
            monitorStatisticsVOList = new ArrayList<>();
        }
        Integer total = baseMapper.getCandidateTotalByExamId(examId);
        monitorStatisticsVOList.add(MonitorStatisticsVO.builder()
                .examId(examId)
                .statisticsType(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getCode())
                .statisticsTypeDesc(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getMsg())
                .num(Long.valueOf(total))
                .build());
        rebuildStatisticsList(examId, monitorStatisticsVOList);
        return monitorStatisticsVOList;
    }

    @Override
    public List<MonitorStatisticsVO> findMonitorStatisticsListWithNullRoomId(String examId) {
        List<MonitorStatisticsVO> monitorStatisticsVOList;
        List<KwMonitorStatistics> resList = baseMapper.findMonitorStatisticsListWithNullRoomId(examId);
        if (!resList.isEmpty()) {
            monitorStatisticsVOList = resList.stream().map(vo -> MonitorStatisticsVO.builder()
                    .examId(examId)
                    .statisticsType(vo.getStatisticsType())
                    .statisticsTypeDesc(MonitorStatisticsTypeCode.findByCode(vo.getStatisticsType()).getMsg())
                    .num(vo.getTotal())
                    .build()).collect(Collectors.toList());
        } else {
            monitorStatisticsVOList = new ArrayList<>();
        }
        Integer total = baseMapper.getCandidateTotalByExamId(examId);
        monitorStatisticsVOList.add(MonitorStatisticsVO.builder()
                .examId(examId)
                .statisticsType(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getCode())
                .statisticsTypeDesc(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getMsg())
                .num(Long.valueOf(total))
                .build());
        rebuildStatisticsList(examId, monitorStatisticsVOList);
        return monitorStatisticsVOList;
    }

    @Override
    public List<MonitorStatisticsVO> findMonitorStatisticsList(String examId, List<String> roomIds) {
        List<MonitorStatisticsVO> monitorStatisticsVOList;
        List<KwMonitorStatistics> resList = baseMapper.findMonitorStatisticsListByRoomIds(examId, roomIds);
        if (!resList.isEmpty()) {
            monitorStatisticsVOList = resList.stream().map(vo -> MonitorStatisticsVO.builder()
                    .examId(examId)
                    .statisticsType(vo.getStatisticsType())
                    .statisticsTypeDesc(MonitorStatisticsTypeCode.findByCode(vo.getStatisticsType()).getMsg())
                    .num(vo.getTotal())
                    .build()).collect(Collectors.toList());
        } else {
            monitorStatisticsVOList = new ArrayList<>();
        }
        Integer total = baseMapper.getCandidateTotalByExamId(examId);
        monitorStatisticsVOList.add(MonitorStatisticsVO.builder()
                .examId(examId)
                .statisticsType(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getCode())
                .statisticsTypeDesc(MonitorStatisticsTypeCode.EXAM_CANDIDATE_TOTAL_NUM.getMsg())
                .num(Long.valueOf(total))
                .build());
        rebuildStatisticsList(examId, monitorStatisticsVOList);
        return monitorStatisticsVOList;
    }

    @Override
    public List<MonitorStatisticsVO> findMonitorStatisticsList(LocalDateTime startTime, LocalDateTime endTime) {
        List<KwMonitorStatistics> resList = baseMapper.findMonitorStatisticsListBetweenDate(startTime, endTime);
        if (!resList.isEmpty()) {
            return resList.stream().map(vo -> MonitorStatisticsVO.builder()
                    .examId(vo.getExamId())
                    .roomId(vo.getRoomId())
                    .statisticsType(vo.getStatisticsType())
                    .statisticsTypeDesc(MonitorStatisticsTypeCode.findByCode(vo.getStatisticsType()).getMsg())
                    .num(vo.getTotal())
                    .build()).collect(Collectors.toList());
        }
        return Collections.EMPTY_LIST;
    }

    public void rebuildStatisticsList(String examId, List<MonitorStatisticsVO> monitorStatisticsVOList) {
        Map<Integer, String> statisticsMap =  MonitorStatisticsUtils.convertToMap();
        Map<Integer, MonitorStatisticsVO> monitorStatisticsVOMap = new HashMap<>();
        monitorStatisticsVOList.forEach(vo -> {
            if (monitorStatisticsVOMap.containsKey(vo.getStatisticsType())) {
                MonitorStatisticsVO monitorStatisticsVO = monitorStatisticsVOMap.get(vo.getStatisticsType());
                monitorStatisticsVO.setNum(monitorStatisticsVO.getNum() + vo.getNum());
            } else {
                monitorStatisticsVOMap.put(vo.getStatisticsType(), vo);
            }
        });
        List<MonitorStatisticsVO> addList = statisticsMap.entrySet().stream().filter(m -> !monitorStatisticsVOMap.containsKey(m.getKey())).map(m -> MonitorStatisticsVO.builder()
                .examId(examId)
                .statisticsType(m.getKey())
                .statisticsTypeDesc(m.getValue())
                .num(0L)
                .build()).collect(Collectors.toList());
        monitorStatisticsVOList.addAll(addList);
    }

    @Override
    public MonitorStatisticsVO getMonitorStatistics(String examId, Integer statisticsType) {
        KwMonitorStatistics monitorStatistics = baseMapper.selectOne(Wrappers.<KwMonitorStatistics>lambdaQuery()
                .eq(KwMonitorStatistics::getExamId, examId)
                .eq(KwMonitorStatistics::getStatisticsType, statisticsType).last("limit 1"));
        if (Objects.nonNull(monitorStatistics)) {
            return MonitorStatisticsVO.builder()
                    .id(monitorStatistics.getId())
                    .examId(examId)
                    .statisticsType(monitorStatistics.getStatisticsType())
                    .num(monitorStatistics.getTotal())
                    .build();
        }
        return null;
    }

    @Override
    public MonitorStatisticsVO getMonitorStatisticsByRoomId(String examId, String roomId, Integer statisticsType) {
        KwMonitorStatistics monitorStatistics = baseMapper.selectOne(Wrappers.<KwMonitorStatistics>lambdaQuery()
                .eq(KwMonitorStatistics::getExamId, examId)
                .eq(KwMonitorStatistics::getRoomId, roomId)
                .eq(KwMonitorStatistics::getStatisticsType, statisticsType).last("limit 1"));
        if (Objects.nonNull(monitorStatistics)) {
            return MonitorStatisticsVO.builder()
                    .id(monitorStatistics.getId())
                    .examId(examId)
                    .roomId(roomId)
                    .statisticsType(monitorStatistics.getStatisticsType())
                    .num(monitorStatistics.getTotal())
                    .build();
        }
        return null;
    }

    @Override
    public Integer getCount(String examId, String roomId, Integer statisticsType) {
        return baseMapper.selectCount(Wrappers.<KwMonitorStatistics>lambdaQuery()
                .eq(KwMonitorStatistics::getExamId, examId)
                .eq(KwMonitorStatistics::getRoomId, roomId)
                .eq(KwMonitorStatistics::getStatisticsType, statisticsType));
    }

}
